home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Visual Basic 5.0 (2nd Edition) / Hardcore Visual Basic 5.0 - Second Edition (1997)(Microsoft Press).iso / Code / Goodies / CallBack / CALLBACK.TXT < prev    next >
Text File  |  1997-06-10  |  15KB  |  337 lines

  1. ***********************************
  2. What is the VB Callback Server Dll?
  3. ***********************************
  4.  
  5. The Visual Basic Callback Server Dll allows you to use methods in a VB
  6. class module as callbacks for standard Windows API calls. Exposing
  7. callback entry points in Visual Basic allows you to easily use the
  8. EnumWindows, EnumFonts, and other windows enumeration functions
  9. without writing a C Dll to handle the function pointers required for
  10. these API calls. Other applications include timers without forms,
  11. controlling a dialog resource from VB, and installing Windows hooks.
  12. VBA is not thread-safe, so callbacks for threading aren't supported.
  13.  
  14. Since window procedures are considered callback functions, the
  15. callback server can also be used to subclass windows (in the same
  16. process, cross-process subclassing isn't supported). You can also use
  17. the Message Blaster OCX for subclassing. While MsgBlast is easier to
  18. use for the programmer making an early attempt at directly
  19. manipulating windows message and supports cross-process subclassing,
  20. the callback server has much less overhead. MsgBlast doesn't support
  21. callbacks, just subclassing of a window handle.
  22.  
  23. ******
  24. Setup?
  25. ******
  26.  
  27. To install the callback server, copy CBack32.Dll to the Windows\System
  28. directory (or System32 on Windows NT) and run regsvr32 CBack32.Dll.
  29. Regsvr32 can be found in the Tools\RegUtils directory on the VB5 CD. If you
  30. will be using the callback server to subclass and will be using break
  31. mode in VB, you should also copy VBBrk32.Dll to the system[32]
  32. directory. VBBrk32.Dll isn't required (in fact, it will not load) for
  33. a VB executable. VBBrk32 failing to load will cause Regsvr32 to post
  34. an error message, but you won't get this error when using CBack32 from
  35. a VB executable. If you want to insure CBack32 is properly installed
  36. on your client machine, add the following code to your program
  37. (assuming you have a reference to VB CallBack Server).
  38.  
  39. Private Declare Function DllRegisterServer Lib "CBack32.Dll" () As Long
  40.  
  41. Sub Main()
  42. Dim CBackGen As CallBackGenerator
  43.     On Error Resume Next
  44.     Set CBackGen = New CallBackGenerator
  45.     If Err Then
  46.         DllRegisterServer
  47.         Err.Clear
  48.         Set CBackGen = New CallBackGenerator
  49.     End If
  50.     If Err Then MsgBox "This program requires CBack32.Dll": End
  51. End Sub
  52.  
  53. ****************
  54. How do I use it?
  55. ****************
  56.  
  57. To help you understand how to create a callback class module, we'll
  58. step through creating a class module to provide an EnumWindowsProc
  59. callback for the EnumWindows API call. We'll go over the details of
  60. each of the calls after you've seen the initial steps:
  61.  
  62. 1. Add a reference to the Callback Server to your project.
  63.  
  64. 2. Insert a class module into your project.
  65.  
  66. 3. In the object browser, look at the VBCallBackType enumeration in
  67. the VBCallBack type library.
  68.  
  69. 4. Select CBType_WNDENUMPROC and look at the description of the
  70. required method. You'll see (ByVal Long, Any) As Long. This
  71. information, together with the windows API help file for EnumWindows,
  72. will help you create a correct function prototype.
  73.  
  74. 5. Make sure your editor is in Full Module View. You can change this
  75. setting using the Editor tab of the Options dialog.
  76.  
  77. 6. Type the following into your class module:
  78.  
  79. Public Function WNDENUMPROC(ByVal hWnd As Long, LParam As ListBox) As Long
  80.  
  81. (The last parameter is user defined, we'll pass in a listbox just
  82. because we can).
  83.  
  84. 7. Before the first method, type:
  85.  
  86. Private m_CallBack As CallBack
  87.  
  88. 8. Add a Class_Initialize event. In the Class_Initialize event, type
  89.  
  90. Set m_CallBack = NewCallBack(CBType_WNDENUMPROC, Me, True)
  91.  
  92. 9. Add a read-only ProcAddress property as follows:
  93.  
  94. Public Property Get ProcAddress() As Long
  95.     ProcAddress = m_CallBack.ProcAddress
  96. End Property
  97.  
  98. You're now ready to create an instance of your class. After you create
  99. an instance of the class, you can pass the ProcAddress property of
  100. your class to the EnumWindows API call, and EnumWindows will call your
  101. WNDENUMPROC method.
  102.  
  103. ************************************
  104. What is the first method of a class?
  105. ************************************
  106.  
  107. The first method of a class is the first public entry in a class
  108. module. The callback server will only work if the first method is
  109. correctly defined. Public variables aren't allowed before the first
  110. method. To make sure the method you're defining is listed back first,
  111. be sure to use Full Module View. If you screw this up, you'll crash.
  112.  
  113. VBHandler:
  114.  
  115.    The VBHandler property of the CallBack class is used to set the VB
  116.    class instance which will be used to handle the callback.
  117.  
  118. ProcAddress:
  119.  
  120.    The ProcAddress property returns the address you should use as the
  121.    function pointer for your VB class.
  122.  
  123. Persistent ProcAddress, IsPersistent:
  124.  
  125.    Some callback addresses are used by a single API call. For example,
  126.    you only have to count on the address of an enum procedure for the
  127.    duration of the enumeration call. Other addresses, however, must
  128.    remain constant for an indefinite amount of time. For example, a
  129.    WindowProc stays the same as long as the window remains alive and
  130.    is a persistent callback type. You can use the IsPersistent
  131.    property of a callback object to see if the VBHandler class you
  132.    assigned will always be called for the given procaddress. If a
  133.    callback is persistent, then you can only create 32 instances of
  134.    the callback object (32 was just as easy as 16).
  135.  
  136. ResetCallBack:
  137.  
  138.    If a callback address isn't persistent, then the same address is
  139.    used for each instance of a callback with a given type. This means
  140.    that the class instance which is being pointed to by the internal
  141.    callback routine may be invalid if you have more than one instance
  142.    of the same non-persistent callback type. Reading the ProcAddress
  143.    property or calling the ResetCallBack method will guarantee that
  144.    the next call will be mapped to the correct class instance. For
  145.    example, if you are enumerating top-level windows and se a second
  146.    class to enumerate child windows, you will need to do an
  147.    m_CallBack.ResetCallBack before you return from the outer
  148.    EnumWindows call. If you don't, then the next call made by
  149.    EnumWindows will actually call the wrong enumerator class. In the
  150.    sample code, this isn't an issue because the same enumerator is
  151.    used recursively for top-level and child windows.
  152.  
  153. NewCallBack:
  154.  
  155.    The NewCallBack method of the CallBackGenerator class is used to
  156.    create a callback object. You can't use the New keyword directly to
  157.    create a callback object. The first parameter, which is required,
  158.    specifies the type of callback object to be created. Note that
  159.    NewCallBack will fail if you ask for more than 32 instances or a
  160.    persistent callback type. The valid values for the Type parameter
  161.    are listed in the VBCallBackType enumeration. If the callback type
  162.    you use doesn't match your first method, you'll crash. The
  163.    VBHandler optional parameter is used as a shortcut for setting the
  164.    VBHandler property. Pass in a reference to your class module. The
  165.    Contained optional parameter, which takes a boolean value, is a
  166.    little harder to understand. If Contained is true, then NewCallBack
  167.    does not increment the reference count (ie, AddRef), the object
  168.    specified by the VBHandler parameter. This enables you to create a
  169.    class module which uses a callback object and can be destroyed with
  170.    a Set obj=Nothing call. If you don't use the contained flag, a Quit
  171.    (or similar method) is required to clean up the object correctly.
  172.    If you try to persist the callback object beyond the lifetime of
  173.    the initial VBHandler, you must reset VBHandler. If you don't, you
  174.    will crash. Normally, the call to NewCallBack is made in the
  175.    Class_Initialize event and looks like:
  176.  
  177.    Set m_CallBack = NewCallBack(CBType_WNDPROC, Me, True)
  178.  
  179. *****************
  180. How does it work?
  181. *****************
  182.  
  183. The concept behind the callback server is very simple. Visual Basic
  184. classes are interfaces which are derived from IDispatch. A VB class
  185. has a VTable, and you can generate a C++ header file which describes
  186. the VTable. Given an instance of a VB class, you can call its methods
  187. from C++ using the VTable. For example, the class for the WNDPROC
  188. looks something like:
  189.  
  190. class CVBWNDPROC : IDispatch
  191. {
  192.     STDMETHOD(CB_WNDPROC)(HWND hwnd, UINT msg, WPARAM wParam,
  193.                           LPARAM lParam, LRESULT FAR*) = 0;
  194. };
  195.  
  196. Reliance on the VTable is the reason why correctly defining the first
  197. method of the VB class is so important. The callback server doesn't
  198. care what the remaining methods and properties are, so long as the
  199. first method matches the class.
  200.  
  201. In order to have a callback, you must have a procedure address. Since
  202. member functions of classes can't be used reliably as function
  203. pointers (the implicit this pointer screws things up), the callback
  204. Dll has actual entry points for each defined callback type. The
  205. addresses of these fixed entry points are returned to the ProcAddress
  206. property of the callback class. These procedures simply call through
  207. the VTable into the VB class. For persistent callback types, there are
  208. 32 such entry points. Lets look at the first one:
  209.  
  210. LRESULT WINAPI CB_WNDPROC0 (HWND hwnd,
  211.                             UINT msg,
  212.                             WPARAM wParam,
  213.                             LPARAM lParam)
  214. {
  215.   LRESULT retVal = 0;
  216.   //Break mode checks, see next section
  217.   if (*VBInBreakMode && VBInBreakMode() ) {
  218.     if ( pCWNDPROC[0]->m_pDebugProc )
  219.       return (pCWNDPROC[0]->m_pDebugProc)(hwnd, msg, wParam, lParam); 
  220.     else
  221.       return 0;
  222.   }
  223.   //The real call.  pCWNDPROC is a global array of pointers to
  224.   //CallBack classes.
  225.   if (pCWNDPROC[0]->m_pVBClass) 
  226.       ((CVBWNDPROC FAR*) (pCWNDPROC[0]->m_pVBClass))->
  227.        CB_WNDPROC (hwnd, msg, wParam, lParam, &retVal);
  228.   return retVal;
  229. }
  230.  
  231. ***************************
  232. What happens in break mode?
  233. ***************************
  234.  
  235. Visual Basic doesn't mind it when you call directly into VTables,
  236. unless it happens to be in break mode. If VB is in break mode, it
  237. doesn't expect any VB code to be executed. Calling the VTable directly
  238. bypasses all safety checks to stop code from executing, so the
  239. callback server must check this for you. The functionality for this is
  240. provided in a separate Dll called VBBrk32.Dll. This Dll exposes one
  241. entry point, called VBInBreakMode, which returns a BOOL. The Dll will
  242. fail to load if the callback server isn't running in the Visual Basic
  243. design environment. You can get a pointer to VBInBreakMode using
  244. LoadLibrary and GetProcAddress calls. By declaring a variable as
  245. follows:
  246.  
  247. BOOL (WINAPI *VBInBreakMode)(void) = NULL;
  248.  
  249. You can use the code shown above to determine if VB is in break mode
  250. or not.
  251.  
  252.    DebugProc:
  253.  
  254.    So what happens when VB is in break mode? The answer is simple: the
  255.    VTable call is disallowed. If the callback is being used to
  256.    subclass, this causes a major problem because no windows message
  257.    get through. To work around this, the callback class has a
  258.    DebugProc property which can be set to the function pointer which
  259.    is called in place of the VTable call when VB is in break mode.
  260.    Search the sample code for DebugProc to see this in action.
  261.  
  262. *****************
  263. Debugging Crashes
  264. *****************
  265.  
  266. Since you are making VTable calls, you have to be very careful that
  267. your VTable and parameters are set up correctly. If you are
  268. experiencing crashes when your callback method is called, try to put a
  269. break point on the first (ie, the Sub or Function line) of the
  270. callback method. If you don't reach the breakpoint before crashing,
  271. then you've misplaced the first method. Be sure there are no public
  272. methods, properties, or variables listed before your callback method.
  273. If you reach the break point, but crash if you step off of it, then
  274. your parameters are wrong.
  275.  
  276. If you are running the 16 bit callback server, you may run into
  277. conflicts with VB when the Appearance property of controls on a
  278. subclassed form is set to 1 - 3D. This isn't a problem for the 32 bit
  279. server.
  280.  
  281. ******************************************
  282. Workarounds for parameter type limitations
  283. ******************************************
  284.  
  285. VB classes can't take structures as parameters. This causes some
  286. problems for some callbacks, which pass in structures. When your
  287. callback takes a structure, define the parameter as a ByVal Long and
  288. use CopyMemory to make a copy of the structure. See WM_GETMINMAXINFO
  289. or EnumFontFamProc in the sample project.
  290.  
  291. Another problem occurs with strings. VB strings are actually BSTRs,
  292. but callback strings are passed in as LPSTRs. You can use CopyMemory
  293. to make a copy of the string. See the ResourceEnumerator class in the
  294. sample for an example.
  295.  
  296. ********************************
  297. How to build the callback server
  298. ********************************
  299.  
  300. The code for the callback server is included. Everything is highly
  301. macroized, so you need to modify only the CBackDat.h file to add or
  302. remove callbacks. Beneath the main directory, there are IP32 and IP16
  303. (IP=In-process, I use this for all of my servers). The IP directories
  304. contain the VC mak files. In order to build the project, you first
  305. need to build the TLB and header files using TLB.Bat, which should be
  306. called from the IP32 directory with ..\TLB 32 H before you attempt to
  307. build the 32 bit callback server.
  308.  
  309. Although I've tried to include as many callbacks in the server as I
  310. could find, you may find some that I missed. You may also notice that
  311. the Dll could be smaller because I have many more callbacks defined
  312. than you can ever use in a single project. If you wish to build a
  313. custom version of the callback server, feel free to do so. However,
  314. please call the server something else, rename the file, and change the
  315. GUIDs in CBack.ODL before redistribution of your server.
  316.  
  317. The code for the VBBrk32 project isn't included. This Dll will need to
  318. be replaced for future version of Visual Basic (hence the generic
  319. name). Any code you derive from reverse engineering this Dll is
  320. explicitly not supported. You can use it the same way I used it and
  321. get the same results.
  322.  
  323. *********************
  324. Copyright information
  325. *********************
  326.  
  327. CBack32.Dll and CBack16.Dll can be redistributed freely, as can
  328. derivations of the server. However, the sample code and rights to the
  329. callback server belong to Microsoft Corporation and are copyrighted
  330. with this book. Redistribution of the sample code is prohibited.
  331.  
  332. I hope you enjoy using my callback server. I'd love to hear your
  333. feedback, as well as feature requests for future versions of the
  334. callback server Dll. You can contact me MattCur@Microsoft.Com.
  335.  
  336.     Matthew Curland
  337.